/*
 * Copyright (C) 2011, EADS France
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 */
package xowl.interpreter.backends.rdf;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import xowl.interpreter.backends.rete.Condition;
import xowl.interpreter.backends.rete.RETERule;
import xowl.interpreter.backends.rete.Token;
import xowl.interpreter.backends.rete.TokenActivable;

/**
 *
 * @author Laurent WOUTERS
 */
public class SimpleQueryEngine extends RDFQueryEngine {
    public SimpleQueryEngine(RDFRepository repo) {
        super(repo);
    }
    
    @Override protected xowl.lang.runtime.Value eval_Contains(xowl.interpreter.impl.Evaluator parent, xowl.lang.actions.Contains query) throws xowl.interpreter.XOWLException {
        if (query.getAllAxioms().size() == 1 && query.getAllAxioms().iterator().next() instanceof xowl.lang.actions.FunctionDefinition)
            return contains_FunctionDefinition(parent, repository, (xowl.lang.actions.FunctionDefinition)query.getAllAxioms().iterator().next());
        Job job = new Job(parent);
        return job.execute(query);
    }
    
    @Override public List<xowl.interpreter.QuerySolution> select(xowl.interpreter.impl.Evaluator parent, xowl.lang.actions.Select query) throws xowl.interpreter.XOWLException {
        if (query.getAllAxioms().size() == 1 && query.getAllAxioms().iterator().next() instanceof xowl.lang.actions.FunctionDefinition) {
            return select_FunctionDefinition(parent, repository, (xowl.lang.actions.FunctionDefinition)query.getAllAxioms().iterator().next());
        } else {
            List<xowl.interpreter.QuerySolution> results = new ArrayList<xowl.interpreter.QuerySolution>();
            Job job = new Job(parent);
            results.addAll(job.execute(query));
            return results;
        }
    }
    
    private class Job {
        private xowl.interpreter.impl.AggregatedEvaluator evaluator;
        private boolean found;
        private List<xowl.interpreter.QuerySolution> solutions;
        public Job(xowl.interpreter.impl.Evaluator evaluator) {
            this.evaluator = (xowl.interpreter.impl.AggregatedEvaluator)evaluator;
        }
        
        public xowl.lang.runtime.Value execute(final xowl.lang.actions.Contains query) throws xowl.interpreter.XOWLException {
            Translation translation = repository.getXOWLTranslator().translateAxioms(evaluator, true, true, query.getAllAxioms());
            RETERule rule = new RETERule(new TokenActivable() {
                @Override public void activateToken(Token token) {
                    if (!verifiesConditions(query, token.getBindings()))
                        return;
                    found = true;
                }
                @Override public void deactivateToken(Token token) { }
                @Override public void activateTokens(Collection<Token> tokens) {
                    for (Token token : tokens)
                        activateToken(token);
                }
                @Override public void deactivateTokens(Collection<Token> tokens) { }
            });
            for (RDFTriple triple : translation.getTriples())
                rule.getPositives().add(new Condition(null, triple));
            applyChanges();
            network.addRule(rule);
            network.removeRule(rule);
            return xowl.utils.Values.getValue(repository, found);
        }
        
        public List<xowl.interpreter.QuerySolution> execute(final xowl.lang.actions.Select query) throws xowl.interpreter.XOWLException {
            solutions = new ArrayList<xowl.interpreter.QuerySolution>();
            Translation translation = repository.getXOWLTranslator().translateAxioms(evaluator, true, true, query.getAllAxioms());
            RETERule rule = new RETERule(new TokenActivable() {
                @Override public void activateToken(Token token) {
                    Map<XOWLVariableNode, RDFNode> bindings = token.getBindings();
                    if (!verifiesConditions(query, bindings))
                        return;
                    Map<xowl.lang.actions.QueryVariable, xowl.lang.runtime.Value> data = new HashMap<xowl.lang.actions.QueryVariable, xowl.lang.runtime.Value>();
                    for (Entry<XOWLVariableNode, RDFNode> entry : bindings.entrySet()) {
                        try { data.put(entry.getKey().getVariable(), repository.getValue(entry.getValue())); }
                        catch (xowl.interpreter.XOWLException ex) { }
                    }
                    solutions.add(new xowl.interpreter.QuerySolution(data));
                }
                @Override public void deactivateToken(Token token) { }
                @Override public void activateTokens(Collection<Token> tokens) {
                    for (Token token : tokens)
                        activateToken(token);
                }
                @Override public void deactivateTokens(Collection<Token> tokens) { }
            });
            for (RDFTriple triple : translation.getTriples())
                rule.getPositives().add(new Condition(null, triple));
            applyChanges();
            network.addRule(rule);
            network.removeRule(rule);
            return solutions;
        }
        
        private boolean verifiesConditions(xowl.lang.actions.Query query, Map<XOWLVariableNode, RDFNode> bindings) {
            if (query.getAllGuards().isEmpty())
                return true;
            QueryVariableEvaluator qve = new QueryVariableEvaluator(repository, bindings);
            evaluator.addPart(qve);
            for (xowl.lang.owl2.LiteralExpression guard : query.getAllGuards()) {
                try {
                    if (!xowl.utils.Values.isTrue(evaluator.evalLiteral(evaluator, guard))) {
                        evaluator.removePart(qve);
                        return false;
                    }
                } catch (xowl.interpreter.XOWLException ex) {
                    evaluator.removePart(qve);
                    return false;
                }
            }
            evaluator.removePart(qve);
            return true;
        }
    }
}
